#pragma once
#include <cerrno>
#include <cstring>
#include <iostream>
#include <functional>
#include <unistd.h>
#include <pthread.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include "task.hpp"
#include "error.hpp"
#include "threadPool.hpp"

namespace ns_tcpserver
{
    // 完成简单的echoServer

    const uint16_t defaultport = 8888;
    const int backlog = 32; // 后面再解释这个参数 -- listen
    using func_t = std::function<std::string(std::string)>;
    class tcpServer; // 申明

    struct ThreadData
    {
        ThreadData(int fd, std::string name, tcpServer *cur) : _sock(fd), _clientname(name), _cur(cur)
        {
        }
        int _sock;
        std::string _clientname;
        tcpServer *_cur;
    };

    class tcpServer
    {
    public:
        tcpServer(func_t cb, uint16_t port = defaultport) : _server(cb), _port(port)
        {
            pthread_mutex_init(&_mutex,nullptr);
            pthread_cond_init(&_cond,nullptr);
        }
        void initServer()
        {
            std::cout << "tcpserver init..." << std::endl;
            // 1.创建监听套接字
            _listensock = socket(AF_INET, SOCK_STREAM, 0);
            if (_listensock < 0)
            {
                // strerror:打印错误码对应的错误信息
                std::cerr << "socket failed..." << strerror(errno) << std::endl;
                exit(SOCKET_ERR);
            }
            // 2.绑定ip,port
            // 2.1.先填充server,注意转化为网络序列，注意ip地址不能固定
            struct sockaddr_in server;
            server.sin_family = AF_INET;
            server.sin_port = ntohs(_port);
            server.sin_addr.s_addr = ntohl(INADDR_ANY);
            if (bind(_listensock, (struct sockaddr *)&server, sizeof(server)) < 0)
            {
                std::cerr << "bind failed..." << strerror(errno) << std::endl;
                exit(BIND_ERR);
            }
            // 3.监听
            if (listen(_listensock, backlog) < 0)
            {
                std::cerr << "listen failed..." << strerror(errno) << std::endl;
                exit(LISTEN_ERR);
            }
            // 到这里初始化工作就完成了
        }
        //采用方案2试试

        // void getResult(std::string str)
        // {
        //     _response = str;
        //     // cout << "我已经写回结果..." << _response << std:: endl;
        //     pthread_cond_signal(&_cond);
        // }

        // 处理服务也是死循环！ 你不可能只读取一次就结束了吧
        void service(int &sock, std::string &name)
        {
            //让线程池，去处理短期任务。
            threadPool<Task>* tp = threadPool<Task>::getInstance();

            char buff[1024] = {0};
            while (true)
            {
                // 1.从对应的套接字中获取数据  -- 获取数据
                ssize_t n = read(sock, buff, sizeof(buff) - 1); //-1是当作字符串处理为/0预留位置
                if (n > 0)
                {
                    buff[n] = 0;
                    std::cout << name << ">>>" << buff << std::endl;
                    // 2.业务处理
                    // 构建任务，交给线程池处理的任务队列

                    Task t(buff,_server, _cond);
                    tp->pushTask(t);
                    //正确的解决线程之间的同步问题应该是要使用条件变量！！！
                    static int cnt = 0;
                    while(t.getRes().empty())
                        pthread_cond_wait(&_cond,&_mutex);
                        
                    write(sock,t.getRes().c_str(),t.getRes().size());

                }
                else if (n == 0)
                {
                    // 在tcp通信中，因为tcp是建立连接的，accept可以知道连接的建立，那什么时候知道连接关闭呢？
                    // 当对端关闭的时候，read会读到0个字符。所以当n==0时意味着对端关闭
                    // 我们要做的是关闭文件描述符
                    close(sock);
                    std::cout << name << "close,meto" << std::endl;
                    break;
                }
                else
                {
                    // n < 0,意味着读取错误
                    close(sock);
                    std::cerr << "read failed..." << strerror(errno) << std::endl;
                    break;
                }
            }
        }
        
        static void* threadRoutine(void* args)
        {
            ThreadData* t = static_cast<ThreadData*>(args);
            t->_cur->service(t->_sock,t->_clientname);
        }
        
        void start()
        {
            std::cout << "tcpserver start..." << std::endl;

            while (true)
            {
                // 4.获取连接
                struct sockaddr_in client;
                memset(&client, 0, sizeof(client));
                socklen_t len = sizeof(client);
                int sock = accept(_listensock, (struct sockaddr *)&client, &len); // 默认是阻塞式的
                if (sock < 0)
                {
                    std::cerr << "accept failed..." << strerror(errno) << std::endl;
                    continue;
                }
                // accept返回的套接字，才是真正给客户端提供服务的套接字！
                // 5.连接成功

                // 获取客户端信息
                std::string clientip = inet_ntoa(client.sin_addr);
                uint16_t clientport = ntohs(client.sin_port);
                std::string clientname = clientip + "-";
                clientname += std::to_string(clientport);
                // debug测试
                std::cout << "accept success... " << sock << " from " << _listensock << " " << clientname << std::endl;
                // service(sock, clientname); // 提供服务，进行收取数据，数据处理，发送数据

                // 不能去改Thread.hpp这个组件，因为你在threadPool.hpp中固定了它的用法，你只能使用原生线程库
                pthread_t t;
                //data是必须是new出来的，不然会导致多个线程使用的是同一个data
                ThreadData *data = new ThreadData(sock, clientname, this);
                pthread_create(&t, nullptr, threadRoutine, data);

            }
        }

        ~tcpServer()
        {
            pthread_mutex_destroy(&_mutex);
            pthread_cond_destroy(&_cond);
        }

    private:
        uint16_t _port;
        int _listensock;
        func_t _server;
        //因为时序的问题，不能保证先执行业务处理，再write，我们使用回调，将结果写会来
        // std::string _response; //通过回调，从而获得处理的结果
        //上面的回调并不能解决问题，我们必须通过线程同步去解决它们步调不一致的问题
        pthread_cond_t _cond;
        pthread_mutex_t _mutex;

    };
}